#include "PartBoundCond.h"

#include <cstdlib>

#include <iostream>
#include <string>

#include <cmath>

#include "CellParticles.h"
#include "PartBoundCondType.h"
#include "../Parallel/ParallelMPI.h"
#include "../Tool/Macros.h"

using namespace std;

template<int dim>
PartBoundCond<dim>::PartBoundCond(PicParams* params_, ParallelMPI* pmpi_, int ispec) :
    params(params_),
    pmpi(pmpi_)
{
    //cout<<"part bound cond begin"<<endl;
    //number of dimensions for the particle
    //\todo (MG to JD) isn't it always 3?
    n_dim_field = params->n_dim_field;

    //Absolute global values
    double x_min_global;
    double x_max_global;
    double y_min_global;
    double y_max_global;
    double z_min_global;
    double z_max_global;

    x_min_global = 0;
    x_max_global = params->cell_length[0]*(params->dims_space_global[0]);
    if(n_dim_field > 1)
    {
        y_min_global = 0;
        y_max_global = params->cell_length[1]*(params->dims_space_global[1]);
    }
    if(n_dim_field > 2)
    {
        z_min_global = 0;
        z_max_global = params->cell_length[2]*(params->dims_space_global[2]);
    }

    //by default apply no bcs
    bc_west   = NULL;
    bc_east   = NULL;
    bc_south  = NULL;
    bc_north  = NULL;
    bc_bottom = NULL;
    bc_up     = NULL;

    //-----------------------------
    //Define limits of local domain
    //-----------------------------

    //1d3v or 2d3v or 3d3v
    x_min = max( x_min_global, pmpi->getDomainLocalMin(0) );
    x_max = min( x_max_global, pmpi->getDomainLocalMax(0) );

    //cout<<"x_max_global "<<pmpi->getDomainLocalMax(0)<<endl;

    //2d3v or 3d3v
    if ( n_dim_field > 1 ) {
        if ( (params->bc_em_type_y[0]=="periodic") || (params->bc_em_type_y[1]=="periodic") ) {
            y_min = pmpi->getDomainLocalMin(1);
            y_max = pmpi->getDomainLocalMax(1);
        }
        else {
            y_min = max( y_min_global, pmpi->getDomainLocalMin(1) );
            y_max = min( y_max_global, pmpi->getDomainLocalMax(1) );
        }
    }

    //3d3v
    if ( n_dim_field > 2 ) {
        if ( (params->bc_em_type_z[0]=="periodic") || (params->bc_em_type_z[1]=="periodic") ) {
            z_min = pmpi->getDomainLocalMin(2);
            z_max = pmpi->getDomainLocalMax(2);
        }
        else {
            z_min = max( z_min_global, pmpi->getDomainLocalMin(2) );
            z_max = min( z_max_global, pmpi->getDomainLocalMax(2) );
        }
    }

    //Check for inconsistencies between EM and particle BCs
    if ( ((params->bc_em_type_x[0]=="periodic")&&(params->species_param[ispec].bc_part_type_west!="periodic"))
     ||  ((params->bc_em_type_x[1]=="periodic")&&(params->species_param[ispec].bc_part_type_east!="periodic")) ) {
        log_error<<"For species #" << ispec << ", periodic EM boundary conditions require x particle BCs to be periodic.";
    }
    if ( n_dim_field > 1 ) {
        if ( ((params->bc_em_type_y[0]=="periodic")&&(params->species_param[ispec].bc_part_type_south!="periodic"))
         ||  ((params->bc_em_type_y[1]=="periodic")&&(params->species_param[ispec].bc_part_type_north!="periodic")) ) {
            log_error<<"For species #" << ispec << ", periodic EM boundary conditions require y particle BCs to be periodic.";
        }
        if ( n_dim_field > 2 ) {
            if ( ((params->bc_em_type_z[0]=="periodic")&&(params->species_param[ispec].bc_part_type_bottom!="periodic"))
             ||  ((params->bc_em_type_z[1]=="periodic")&&(params->species_param[ispec].bc_part_type_up!="periodic"    )) ) {
                log_error<<"For species #" << ispec << ", periodic EM boundary conditions require z particle BCs to be periodic.";
            }
        }
    }

    //----------------------------------------------
    //Define the kind of applied boundary conditions
    //----------------------------------------------

    bool thermCond = false;

    //West
    if ( params->species_param[ispec].bc_part_type_west == "refl" ) {
        if (pmpi->isWestern()) bc_west = &refl_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_west == "supp" ) {
        if (pmpi->isWestern()) bc_west = &supp_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_west == "periodic" ) {
        if (pmpi->isWestern()) bc_west = &periodic_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_west == "thermalize" ) {
        thermCond = true;
        if (pmpi->isWestern()) bc_west = &thermalize_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_west == "none" ) {
        log_warning<<"West boundary condition for species " << ispec << " is 'none', which means the same as fields";
    }
    else {
        log_error<<"West boundary condition undefined";
    }

    //East
    if ( params->species_param[ispec].bc_part_type_east == "refl" ) {
        if (pmpi->isEastern()) bc_east = &refl_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_east == "supp" ) {
        if (pmpi->isEastern()) bc_east = &supp_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_east == "periodic" ) {
        if (pmpi->isEastern()) bc_east = &periodic_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_east == "thermalize" ) {
        thermCond = true;
        if (pmpi->isEastern()) bc_east = &thermalize_particle;
    }
    else if ( params->species_param[ispec].bc_part_type_east == "none" ) {
        log_warning<<"East boundary condition for species " << ispec << " is 'none', which means the same as fields";
    }
    else {
        log_error<<"East boundary condition undefined";
    }


    if ( n_dim_field > 1 ) {
        //South
        if ( params->species_param[ispec].bc_part_type_south == "refl" ) {
            if (pmpi->isSouthern()) bc_south = &refl_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_south == "supp" ) {
            if (pmpi->isSouthern()) bc_south = &supp_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_south == "periodic" ) {
            if (pmpi->isSouthern()) bc_south = &periodic_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_south == "thermalize" ) {
            thermCond = true;
            if (pmpi->isSouthern()) bc_south = &thermalize_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_south == "none" ) {
            log_warning<<"South boundary condition for species " << ispec << " is 'none', which means the same as fields";
        }
        else {
            log_error<<"South boundary condition undefined : " << params->species_param[ispec].bc_part_type_south;
        }

        //North
        if ( params->species_param[ispec].bc_part_type_north == "refl" ) {
            if (pmpi->isNorthern()) bc_north = &refl_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_north == "supp" ) {
            if (pmpi->isNorthern()) bc_north = &supp_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_north == "periodic" ) {
            if (pmpi->isNorthern()) bc_north = &periodic_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_north == "thermalize" ) {
            thermCond = true;
            if (pmpi->isNorthern()) bc_north = &thermalize_particle;
        }
        else if ( params->species_param[ispec].bc_part_type_north == "none" ) {
            log_warning<<"North boundary condition for species " << ispec << " is 'none', which means the same as fields";
        }
        else {
            log_error<<"North boundary condition undefined : " << params->species_param[ispec].bc_part_type_north;
        }


        if ( n_dim_field > 2 ) {
            if ( params->species_param[ispec].bc_part_type_bottom == "refl" ) {
                if (z_min==z_min_global) bc_bottom = &refl_particle;
            }
            else if ( params->species_param[ispec].bc_part_type_bottom == "supp" ) {
                if (z_min==z_min_global) bc_bottom = &supp_particle;
            }
            else if ( params->species_param[ispec].bc_part_type_bottom == "periodic" ) {
                if (z_min==z_min_global) bc_bottom = &periodic_particle;
            }

            if ( params->species_param[ispec].bc_part_type_up == "refl" ) {
                if (z_max==z_max_global) bc_up = &refl_particle;
            }
            else if ( params->species_param[ispec].bc_part_type_up == "supp" )  {
                if (z_max==z_max_global) bc_up = &supp_particle;
            }
            else if ( params->species_param[ispec].bc_part_type_up == "periodic" ) {
                if (z_max==z_max_global) bc_up = &periodic_particle;
            }

        }//n_dim_field>2

    }//n_dim_field>1

    /* NOT USED ANYMORE AS WE USE THE ERFINV FCT FROM TOOLS/USERFUNCTIONS
    //---------------------------------------------------------------------
    //Compute the tabulated inverse error function used in thermalizing bcs
    //---------------------------------------------------------------------
    if ( thermCond ) {
        erfinv::instance().prepare();
    }//thermCond
     */

}


template<int dim>
PartBoundCond<dim>::~PartBoundCond()
{
}

template class PartBoundCond<1>;
template class PartBoundCond<2>;
template class PartBoundCond<3>;

